import sys
import cv2
import numpy as np
from PyQt5.QtWidgets import *
from PyQt5.QtGui import QPixmap, QImage, QFont, QIcon
from PyQt5.QtCore import Qt, QThread, pyqtSignal
import random

class GenderPredictor(QThread):
    finished = pyqtSignal(str, int, int)  # gender, x, y

    def __init__(self, face_roi, processed_img, x, y):
        super().__init__()
        self.face_roi = face_roi
        self.processed_img = processed_img
        self.x = x
        self.y = y

    def run(self):
        try:
            # 人脸打码处理（使用更合适的高斯模糊参数）
            blurred_face = cv2.GaussianBlur(self.face_roi, (23, 23), 30)
            self.processed_img[self.y:self.y + blurred_face.shape[0], self.x:self.x + blurred_face.shape[1]] = blurred_face
            self.finished.emit("Face Blurred", self.x, self.y)
        except Exception as e:
            self.finished.emit(f"Error: {str(e)}", self.x, self.y)


class CameraThread(QThread):
    update_frame = pyqtSignal(QImage)

    def __init__(self, face_cascade, eye_cascade, nose_cascade, mouth_cascade, detail_check, gender_check, gray_check, edge_check, contour_check):
        super().__init__()
        self.face_cascade = face_cascade
        self.eye_cascade = eye_cascade
        self.nose_cascade = nose_cascade
        self.mouth_cascade = mouth_cascade
        self.detail_check = detail_check
        self.gender_check = gender_check
        self.gray_check = gray_check
        self.edge_check = edge_check
        self.contour_check = contour_check

    def run(self):
        cap = cv2.VideoCapture(0)
        if not cap.isOpened():
            self.update_frame.emit(QImage())
            return

        while True:
            ret, frame = cap.read()
            if ret:
                gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
                faces = self.face_cascade.detectMultiScale(gray, 1.3, 5)

                for (x, y, w, h) in faces:
                    cv2.rectangle(frame, (x, y), (x + w, y + h), (255, 0, 0), 2)  # 人脸区域用蓝色矩形标记

                    # 人脸打码
                    if self.gender_check.isChecked():
                        face_roi = frame[y:y + h, x:x + w]
                        blurred_face = cv2.GaussianBlur(face_roi, (23, 23), 30)
                        frame[y:y + h, x:x + w] = blurred_face

                    # 详细特征检测
                    if self.detail_check.isChecked():
                        roi_gray = gray[y:y + h, x:x + w]
                        self.detect_features(roi_gray, x, y, frame)

                    # 灰度处理
                    if self.gray_check.isChecked():
                        frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
                        frame = cv2.cvtColor(frame, cv2.COLOR_GRAY2BGR)

                    # 边缘检测
                    if self.edge_check.isChecked():
                        edges = cv2.Canny(gray, 100, 200)
                        frame = cv2.cvtColor(edges, cv2.COLOR_GRAY2BGR)

                    # 轮廓查找与绘制
                    if self.contour_check.isChecked():
                        edges = cv2.Canny(gray, 100, 200)
                        contours, _ = cv2.findContours(edges, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
                        cv2.drawContours(frame, contours, -1, (0, 255, 0), 2)

                rgb_image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
                h, w, ch = rgb_image.shape
                bytes_per_line = ch * w
                q_img = QImage(rgb_image.data, w, h, bytes_per_line, QImage.Format_RGB888)
                self.update_frame.emit(q_img)

        cap.release()

    def detect_features(self, roi_gray, x_offset, y_offset, frame):
        """检测面部特征"""
        # 眼睛检测
        eyes = self.eye_cascade.detectMultiScale(roi_gray)
        for (ex, ey, ew, eh) in eyes:
            cv2.rectangle(frame,
                          (ex + x_offset, ey + y_offset),
                          (ex + x_offset + ew, ey + y_offset + eh),
                          (0, 255, 0), 2)  # 眼睛用绿色矩形标记

        # 鼻子检测
        noses = self.nose_cascade.detectMultiScale(roi_gray, 1.2, 5)
        for (nx, ny, nw, nh) in noses:
            cv2.rectangle(frame,
                          (nx + x_offset, ny + y_offset),
                          (nx + x_offset + nw, ny + y_offset + nh),
                          (255, 165, 0), 2)  # 鼻子用橙色矩形标记

        # 嘴巴检测
        mouths = self.mouth_cascade.detectMultiScale(roi_gray, 1.7, 11)
        for (mx, my, mw, mh) in mouths:
            cv2.rectangle(frame,
                          (mx + x_offset, my + y_offset),
                          (mx + x_offset + mw, my + y_offset + mh),
                          (0, 255, 255), 2)  # 嘴巴用青色矩形标记


class AdvancedFaceProcessor(QWidget):
    def __init__(self):
        super().__init__()
        self.image_path = ""
        self.original_img = None
        self.processed_img = None
        self.threads = []
        self.camera_thread = None
        self.init_ui()
        self.load_models()

    def init_ui(self):
        self.setWindowTitle('高级人脸识别工具')
        self.setGeometry(300, 300, 1000, 800)
        self.setFont(QFont("Arial", 10))

        main_layout = QVBoxLayout()

        # 控制面板
        control_group_box = QGroupBox("控制面板")
        control_layout = QGridLayout()

        # 加载图片按钮
        self.btn_load = QPushButton("加载图片", self)
        self.btn_load.setFixedSize(120, 35)
        self.btn_load.setStyleSheet("background-color: #4CAF50; color: white; font-size: 12px; border: 1px solid #4CAF50;")
        self.btn_load.clicked.connect(self.open_image)
        control_layout.addWidget(self.btn_load, 0, 0)

        # 使用摄像头按钮
        self.btn_camera = QPushButton("使用摄像头", self)
        self.btn_camera.setFixedSize(120, 35)
        self.btn_camera.setStyleSheet("background-color: #FF9800; color: white; font-size: 12px; border: 1px solid #FF9800;")
        self.btn_camera.clicked.connect(self.start_camera)
        control_layout.addWidget(self.btn_camera, 0, 1)

        # 选项标签
        label = QLabel("选项:")
        label.setStyleSheet("font-size: 14px; font-weight: bold;")
        control_layout.addWidget(label, 0, 2, 1, 2, Qt.AlignCenter)

        # 详细特征检测复选框
        self.detail_check = QCheckBox("详细特征检测", self)
        self.detail_check.setChecked(False)
        self.detail_check.setStyleSheet("font-size: 12px;")
        control_layout.addWidget(self.detail_check, 1, 2)

        # 人脸打码复选框
        self.gender_check = QCheckBox("人脸打码", self)
        self.gender_check.setChecked(False)
        self.gender_check.setStyleSheet("font-size: 12px;")
        control_layout.addWidget(self.gender_check, 2, 2)

        # 灰度处理复选框
        self.gray_check = QCheckBox("灰度处理", self)
        self.gray_check.setChecked(False)
        self.gray_check.setStyleSheet("font-size: 12px;")
        control_layout.addWidget(self.gray_check, 3, 2)

        # 边缘检测复选框
        self.edge_check = QCheckBox("边缘检测", self)
        self.edge_check.setChecked(False)
        self.edge_check.setStyleSheet("font-size: 12px;")
        control_layout.addWidget(self.edge_check, 1, 3)

        # 轮廓查找与绘制复选框
        self.contour_check = QCheckBox("轮廓查找与绘制", self)
        self.contour_check.setChecked(False)
        self.contour_check.setStyleSheet("font-size: 12px;")
        control_layout.addWidget(self.contour_check, 2, 3)

        # 开始分析按钮
        self.btn_process = QPushButton("开始分析", self)
        self.btn_process.setFixedSize(120, 35)
        self.btn_process.setStyleSheet("background-color: #2196F3; color: white; font-size: 12px; border: 1px solid #2196F3;")
        self.btn_process.clicked.connect(self.analyze_image)
        control_layout.addWidget(self.btn_process, 0, 4)

        # 添加图标（可选，确保你有图标文件）
        # self.btn_load.setIcon(QIcon('load_icon.png'))
        # self.btn_camera.setIcon(QIcon('camera_icon.png'))
        # self.btn_process.setIcon(QIcon('process_icon.png'))

        control_group_box.setLayout(control_layout)
        main_layout.addWidget(control_group_box)

        # 分隔线
        line = QFrame()
        line.setFrameShape(QFrame.HLine)
        line.setFrameShadow(QFrame.Sunken)
        main_layout.addWidget(line)

        # 图片显示
        self.image_label = QLabel()
        self.image_label.setAlignment(Qt.AlignCenter)
        self.image_label.setStyleSheet("border: 2px solid #666;")
        main_layout.addWidget(self.image_label)

        # 状态栏
        self.status_bar = QLabel("就绪")
        self.status_bar.setStyleSheet("color: #666; font-size: 12px;")
        main_layout.addWidget(self.status_bar)

        # 随机性别显示标签
        self.gender_random_label = QLabel()
        self.gender_random_label.setStyleSheet("color: #666; font-size: 14px; font-weight: bold;")
        main_layout.addWidget(self.gender_random_label)

        self.setLayout(main_layout)

    def load_models(self):
        """加载所有必要的模型和分类器"""
        try:
            # 加载面部特征分类器
            self.face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')
            self.eye_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_eye.xml')
            self.nose_cascade = cv2.CascadeClassifier('haarcascade_mcs_nose.xml')
            self.mouth_cascade = cv2.CascadeClassifier('haarcascade_mcs_mouth.xml')
        except Exception as e:
            QMessageBox.critical(self, "严重错误", f"模型加载失败: {str(e)}")
            sys.exit(1)

    def open_image(self):
        """打开图片文件"""
        path, _ = QFileDialog.getOpenFileName(
            self, "选择图片", "",
            "图片文件 (*.jpg *.jpeg *.png)"
        )
        if path:
            self.image_path = path
            self.status_bar.setText(f"已加载: {path}")
            self.show_original_image()

    def show_original_image(self):
        """显示原始图片"""
        pixmap = QPixmap(self.image_path)
        self.original_img = cv2.imread(self.image_path)
        self.processed_img = self.original_img.copy()
        self.image_label.setPixmap(self.scale_pixmap(pixmap))

    def analyze_image(self):
        """执行分析操作"""
        if not self.image_path:
            QMessageBox.warning(self, "警告", "请先选择图片！")
            return

        try:
            gray = cv2.cvtColor(self.original_img, cv2.COLOR_BGR2GRAY)
            faces = self.face_cascade.detectMultiScale(gray, 1.3, 5)

            if len(faces) == 0:
                QMessageBox.information(self, "提示", "未检测到人脸")
                return

            self.processed_img = self.original_img.copy()
            self.status_bar.setText("分析中...")
            QApplication.processEvents()  # 更新界面

            self.threads = []
            for idx, (x, y, w, h) in enumerate(faces):
                cv2.rectangle(self.processed_img, (x, y), (x + w, y + h), (255, 0, 0), 2)  # 人脸区域用蓝色矩形标记

                # 人脸打码
                if self.gender_check.isChecked():
                    face_roi = self.original_img[y:y + h, x:x + w]
                    predictor = GenderPredictor(face_roi, self.processed_img, x, y)
                    predictor.finished.connect(self.on_thread_finished)
                    predictor.start()
                    self.threads.append(predictor)

                # 详细特征检测
                if self.detail_check.isChecked():
                    roi_gray = gray[y:y + h, x:x + w]
                    self.detect_features(roi_gray, x, y)

            # 灰度处理
            if self.gray_check.isChecked():
                self.processed_img = cv2.cvtColor(self.processed_img, cv2.COLOR_BGR2GRAY)
                self.processed_img = cv2.cvtColor(self.processed_img, cv2.COLOR_GRAY2BGR)

            # 边缘检测
            if self.edge_check.isChecked():
                edges = cv2.Canny(gray, 100, 200)
                self.processed_img = cv2.cvtColor(edges, cv2.COLOR_GRAY2BGR)

            # 轮廓查找与绘制
            if self.contour_check.isChecked():
                edges = cv2.Canny(gray, 100, 200)
                contours, _ = cv2.findContours(edges, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
                cv2.drawContours(self.processed_img, contours, -1, (0, 255, 0), 2)

            # 等待所有线程完成
            for thread in self.threads:
                thread.wait()

            self.status_bar.setText("更新界面...")
            QApplication.processEvents()
            self.show_processed_image()

            # 随机显示性别
            random_gender = random.choice(["男", "女"])
            self.gender_random_label.setText(f"性别: {random_gender}")
            self.status_bar.setText("分析完成")

        except Exception as e:
            QMessageBox.critical(self, "错误", f"分析失败: {str(e)}")

    def detect_features(self, roi_gray, x_offset, y_offset):
        """检测面部特征"""
        # 眼睛检测
        eyes = self.eye_cascade.detectMultiScale(roi_gray)
        for (ex, ey, ew, eh) in eyes:
            cv2.rectangle(self.processed_img,
                          (ex + x_offset, ey + y_offset),
                          (ex + x_offset + ew, ey + y_offset + eh),
                          (0, 255, 0), 2)  # 眼睛用绿色矩形标记

        # 鼻子检测
        noses = self.nose_cascade.detectMultiScale(roi_gray, 1.2, 5)
        for (nx, ny, nw, nh) in noses:
            cv2.rectangle(self.processed_img,
                          (nx + x_offset, ny + y_offset),
                          (nx + x_offset + nw, ny + y_offset + nh),
                          (255, 165, 0), 2)  # 鼻子用橙色矩形标记

        # 嘴巴检测
        mouths = self.mouth_cascade.detectMultiScale(roi_gray, 1.7, 11)
        for (mx, my, mw, mh) in mouths:
            cv2.rectangle(self.processed_img,
                          (mx + x_offset, my + y_offset),
                          (mx + x_offset + mw, my + y_offset + mh),
                          (0, 255, 255), 2)  # 嘴巴用青色矩形标记

    def predict_gender(self, face_roi, processed_img, x, y):
        """启动人脸打码线程"""
        predictor = GenderPredictor(face_roi, processed_img, x, y)
        predictor.finished.connect(self.on_thread_finished)
        predictor.start()
        self.threads.append(predictor)

    def on_thread_finished(self, gender, x, y):
        """处理线程完成后的回调"""
        try:
            cv2.putText(self.processed_img, gender, (x, y - 10),
                        cv2.FONT_HERSHEY_SIMPLEX, 0.8, (255, 0, 0), 2)  # 性别文本用蓝色
        except Exception as e:
            print(f"绘制错误: {str(e)}")

    def start_camera(self):
        """启动摄像头进行人脸识别"""
        if self.camera_thread is not None and self.camera_thread.isRunning():
            self.stop_camera()
            return

        self.camera_thread = CameraThread(
            self.face_cascade, self.eye_cascade, self.nose_cascade, self.mouth_cascade,
            self.detail_check, self.gender_check, self.gray_check, self.edge_check, self.contour_check
        )
        self.camera_thread.update_frame.connect(self.update_image_label)
        self.camera_thread.start()
        self.btn_camera.setText("停止摄像头")
        self.status_bar.setText("摄像头启动")

    def stop_camera(self):
        """停止摄像头"""
        if self.camera_thread is not None and self.camera_thread.isRunning():
            self.camera_thread.terminate()
            self.camera_thread = None
            self.btn_camera.setText("使用摄像头")
            self.status_bar.setText("摄像头停止")

    def update_image_label(self, q_img):
        """更新图片标签"""
        self.image_label.setPixmap(QPixmap.fromImage(q_img))

    def show_processed_image(self):
        """显示处理后的图片"""
        rgb_image = cv2.cvtColor(self.processed_img, cv2.COLOR_BGR2RGB)
        h, w, ch = rgb_image.shape
        bytes_per_line = ch * w
        q_img = QImage(rgb_image.data, w, h, bytes_per_line, QImage.Format_RGB888)
        self.image_label.setPixmap(self.scale_pixmap(QPixmap.fromImage(q_img)))

    def scale_pixmap(self, pixmap):
        """自适应缩放图片"""
        return pixmap.scaled(
            self.image_label.width() - 20,
            self.image_label.height() - 20,
            Qt.KeepAspectRatio,
            Qt.SmoothTransformation
        )

    def resizeEvent(self, event):
        """窗口大小改变事件"""
        super().resizeEvent(event)
        if self.image_path:
            self.show_processed_image()


if __name__ == "__main__":
    app = QApplication(sys.argv)
    app.setStyle("Fusion")
    app.setPalette(QApplication.style().standardPalette())
    window = AdvancedFaceProcessor()
    window.show()
    sys.exit(app.exec_())
